/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 *
 * Contains: IIC driver
 *
 * Change Logs:
 * Date           Author            Notes
 * 2023-1-4       planck           Init
 */

#include <nxos.h>

#include <drivers/iic.h>
#include <drivers/gpio.h>
#include <drivers/pin.h>
#include <regs.h>
#include <base/log.h>
#include <base/driver.h>
#include <base/string.h>
#include <base/clock.h>

enum {
	I2C_STAT_BUS_ERROR	= 0x00,
	I2C_STAT_TX_START	= 0x08,
	I2C_STAT_TX_RSTART	= 0x10,
	I2C_STAT_TX_AW_ACK	= 0x18,
	I2C_STAT_TX_AW_NAK	= 0x20,
	I2C_STAT_TXD_ACK	= 0x28,
	I2C_STAT_TXD_NAK	= 0x30,
	I2C_STAT_LOST_ARB	= 0x38,
	I2C_STAT_TX_AR_ACK	= 0x40,
	I2C_STAT_TX_AR_NAK	= 0x48,
	I2C_STAT_RXD_ACK	= 0x50,
	I2C_STAT_RXD_NAK	= 0x58,
	I2C_STAT_IDLE		= 0xf8,
};




static int f133_i2c_wait_status(void)
{
    // NX_ClockTick cur_tick = NX_ClockTickGet();

    int ret = 0;
    while(1)
    {
        if((Read32(TWI2_ADDR_BASE + TWI_CNTR_OFFSET) & (1 << 3)))
        {
            ret = Read32(TWI2_ADDR_BASE + TWI_STAT_OFFSET);
            break;
        }

        // if(NX_ClockTickGet() - cur_tick > 1)
        // {
        //     ret = I2C_STAT_BUS_ERROR;
        //     break;
        // }
    }
   
    return ret;
}

static int f133_i2c_send_data(NX_U8 dat)
{
	Write32(TWI2_ADDR_BASE + TWI_DATA_OFFSET, dat);
	Write32(TWI2_ADDR_BASE + TWI_CNTR_OFFSET, Read32(TWI2_ADDR_BASE + TWI_CNTR_OFFSET) | (1 << 3));
	return f133_i2c_wait_status();
}

static int f133_i2c_start(void)
{
	NX_U32 val;
    // NX_ClockTick cur_tick = NX_ClockTickGet();

	val = Read32(TWI2_ADDR_BASE + TWI_CNTR_OFFSET);
	val |= (1 << 5) | (1 << 3);
	Write32(TWI2_ADDR_BASE + TWI_CNTR_OFFSET, val);

    while(1)
    {

        if(!(Read32(TWI2_ADDR_BASE + TWI_CNTR_OFFSET) & (1 << 5)))
        {
            break;
        }
			       

        // if(NX_ClockTickGet() - cur_tick > 100)
        // {
        //     break;
        // }
    }
    return f133_i2c_wait_status();
}

static int f133_i2c_stop(void)
{
    NX_U32 val;
    // NX_ClockTick cur_tick = NX_ClockTickGet();

	val = Read32(TWI2_ADDR_BASE + TWI_CNTR_OFFSET);
	val |= (1 << 4) | (1 << 3);
	Write32(TWI2_ADDR_BASE + TWI_CNTR_OFFSET, val);

    while(1)
    {
        if(!(Read32(TWI2_ADDR_BASE + TWI_CNTR_OFFSET) & (1 << 4)))
			break;
        // if(NX_ClockTickGet() - cur_tick > 100)
        // {
        //     break;
        // }
    }
  
    return f133_i2c_wait_status();
}



static int f133_i2c_read(struct i2c_msg * msg)
{
	NX_U8 * p = msg->buf;
	int len = msg->len;
    int ret = len;
    NX_U8 ss;

	if(f133_i2c_send_data((NX_U8)(msg->addr << 1 | 1)) != I2C_STAT_TX_AR_ACK)
    {
        return -1;
    }
		
	Write32(TWI2_ADDR_BASE + TWI_CNTR_OFFSET, Read32(TWI2_ADDR_BASE + TWI_CNTR_OFFSET) | (1 << 2));
	while(len > 0)
	{
		if(len == 1)
		{
			Write32(TWI2_ADDR_BASE + TWI_CNTR_OFFSET, (Read32(TWI2_ADDR_BASE + TWI_CNTR_OFFSET) & ~(1 << 2)) | (1 << 3));
			if(f133_i2c_wait_status() != I2C_STAT_RXD_NAK)
            {
                return -1;
            }
		}
		else
		{
			Write32(TWI2_ADDR_BASE + TWI_CNTR_OFFSET, Read32(TWI2_ADDR_BASE + TWI_CNTR_OFFSET) | (1 << 3));
			if(f133_i2c_wait_status() != I2C_STAT_RXD_ACK)
            {
                return -1;
            }
		}
        ss = Read32(TWI2_ADDR_BASE + TWI_DATA_OFFSET);
		*p++ = ss;
		len--;
	}
	return ret;
}







static int f133_i2c_write(struct i2c_msg * msg)
{
	NX_U8 * p = msg->buf;
	int len = msg->len;

	if(f133_i2c_send_data((NX_U8)(msg->addr << 1)) != I2C_STAT_TX_AW_ACK)
		return -1;
	while(len > 0)
	{
		if(f133_i2c_send_data(*p++) != I2C_STAT_TXD_ACK)
        {
            return -1;
        }
			
		len--;
	}
	return 0;
}

NX_IArch f133_i2c_mst_xfer(struct i2c_msg msgs[], NX_U32 num)
{


    NX_U32 res = 0;
    NX_U32 i = 0;

    if (f133_i2c_start() != I2C_STAT_TX_START)
    {
        return 0;
    }

    for (i = 0; i < num; i++)
    {

        if (i != 0)
        {
            if (f133_i2c_start() != I2C_STAT_TX_RSTART)
                break;
        }

        if (msgs[i].flags & I2C_RD)
            res = f133_i2c_read(&msgs[i]);
        else
            res = f133_i2c_write(&msgs[i]);
    }

    // f133_i2c_stop();

    return i;
}

void i2c_gpio_init(void)
{
    // PE12
    // twi2-sck 2
    // PE13
    // twi2-sda 2
    d1_set_gpio_mode(GPIO_PORT_E, GPIO_PIN_12, 2); // SCK
    d1_set_gpio_mode(GPIO_PORT_E, GPIO_PIN_13, 2); // SDA

    d1_set_gpio_pull(GPIO_PORT_E, GPIO_PIN_12, 1);
    d1_set_gpio_pull(GPIO_PORT_E, GPIO_PIN_13, 1);
}

void i2c_set_rate(void)
{
    Write32((void *)(TWI2_ADDR_BASE + TWI_CCR_OFFSET), 0x28); // 400k
}

void NX_IicDriverInit(void)
{
    Write32((void *)0x0200191c, (0x1 << 18) | (0x1 << 2));
    i2c_gpio_init();
    i2c_set_rate();
    Write32(TWI2_ADDR_BASE + TWI_CNTR_OFFSET, 1 << 6);
    Write32(TWI2_ADDR_BASE + TWI_SRST_OFFSET, 1 << 0);
}

